#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import json
import os
import random
import socket

# 后续加了common中的方法再改
from common.cleaner import clear
from common.common import retry_when_exception, check_command_injection
from common.file_common import log
from common.util.exec_utils import exec_overwrite_file
from tpops.gaussdb.common.gaussdb_common import get_std_in_variable, get_input_dict_in_variable, read_file, \
    get_token_fail_by_wrong_password
from tpops.gaussdb.common.const import EnvName, ErrorCodeType, ErrorCode, VERSION, \
    BACKUP_RATE_LIMIT_MIN, BACKUP_RATE_LIMIT_MAX, BACKUP_RATE_LIMIT_NORMAL, RequestInfo, RequestMethodType, \
    TPOPS_TIMEOUT_SIXTY_SECONDS, GaussDBRdsErrorCode
from tpops.gaussdb.common.gaussdb_restful import get_request_post_http_code, \
    send_request
from tpops.gaussdb.common.const import CertParam, ExtendInfoKeys, RoachConstant, QUERY_SIZE


class ResourceInfo:

    def __init__(self, pid, addr):
        self.pid = pid
        self._addr = addr
        input_dict = get_input_dict_in_variable()
        self.user_name = input_dict.get(f"{EnvName.IAM_USERNAME}_{pid}")
        self.ca_cert_pem = ""
        self.client_key = ""
        self.rand_pass = ""
        if input_dict.get(CertParam.PRO_CERT_PEM + pid):
            self.ca_cert_pem = input_dict.get(CertParam.PRO_CERT_PEM + pid)
            self.client_key = input_dict.get(CertParam.PRO_CERT_KEY + pid)
            self.rand_pass = input_dict.get(CertParam.PRO_CERT_RP + pid)
        elif input_dict.get(CertParam.TAR_CERT_PEM + pid):
            self.ca_cert_pem = input_dict.get(CertParam.TAR_CERT_PEM + pid)
            self.client_key = input_dict.get(CertParam.TAR_CERT_KEY + pid)
            self.rand_pass = input_dict.get(CertParam.TAR_CERT_RP + pid)
        self.db_version = self.get_db_version()
        self.project_id = self.get_project_id()

    @staticmethod
    def get_hostname():
        return socket.gethostname()

    @staticmethod
    @retry_when_exception(retry_times=5, delay=random.randint(15, 30))
    def get_tokens_json(pid, user_name, addr, db_version):
        # 认证鉴权, token认证
        # 待确定管控面端口是否固定
        log.info("start to get_tokens_json")
        if not user_name:
            log.error(f"No user name when get_tokens_json")
        if not addr:
            log.error(f"No business addr")
        if db_version == VERSION.TPOPS:
            url = f"{addr}/APW/APWServlet/aps-autopilot-user-client/openAPI/v1/auth/tokens"
        else:
            url = f"{addr}/rds/v3/auth/mini-token"
        request_header = {'Content-Type': 'application/json'}
        user_password = get_std_in_variable(f"{EnvName.IAM_PASSWORD}_{pid}")
        if not user_password:
            log.error(f"No ps")
        request_body = {"username": user_name, "password": user_password}
        # password 获取方式待确定
        request = RequestInfo(request_url=url, request_body=request_body, request_header=request_header,
                              request_method=RequestMethodType.POST)
        ret, body, header = send_request(request)
        log.error(f"ret: {ret}, body: {body}, header: {header}")
        clear(user_password)
        if db_version == VERSION.TPOPS:
            if not (ret and body.get("code") == "SUCCESS"):
                log.error(f"Failed request for Token{body}!")
                raise Exception("get token failed")
            else:
                log.info("end to get_tokens_json")
                return body
        else:
            if not ret:
                log.error(f"Failed request for Token {body}!")
                raise Exception("get token failed")
            else:
                log.info("end to get_tokens_json")
                return body

    @staticmethod
    @retry_when_exception(retry_times=5, delay=random.randint(15, 30))
    def access_to_tpops(pid, user_name, addr):
        if not user_name:
            log.error(f"No user name when access_to_tpops")
        if not addr:
            log.error(f"No business addr")
        url = f"{addr}/APW/APWServlet/aps-autopilot-user-client/openAPI/v1/auth/tokens"
        request_header = {'Content-Type': 'application/json'}
        user_password = get_std_in_variable(f"{EnvName.IAM_PASSWORD}_{pid}")
        if not user_password:
            log.error(f"No ps")
        request_body = {"username": user_name, "password": user_password}
        http_code, ret_body = get_request_post_http_code(url, request_body, request_header)
        log.info(f"get tpops token http code is {http_code}")
        password_error = get_token_fail_by_wrong_password(ret_body)
        return False if http_code == 404 else True, password_error

    @staticmethod
    @retry_when_exception(retry_times=5, delay=random.randint(15, 30))
    def access_to_convergent(pid, user_name, addr):
        if not user_name:
            log.error(f"No user name when access_to_convergent")
        if not addr:
            log.error(f"No business addr")
        url = f"{addr}/rds/v3/auth/mini-token"
        request_header = {'Content-Type': 'application/json'}
        user_password = get_std_in_variable(f"{EnvName.IAM_PASSWORD}_{pid}")
        if not user_password:
            log.error(f"No ps")
        request_body = {"username": user_name, "password": user_password}
        http_code, ret_body = get_request_post_http_code(url, request_body, request_header)
        log.info(f"get convergent token http code is {http_code}")
        password_error = get_token_fail_by_wrong_password(ret_body)
        return False if http_code == 404 else True, password_error

    @staticmethod
    @retry_when_exception(retry_times=5, delay=random.randint(15, 30))
    def check_token_json(token, addr):
        # token 校验
        url = f"{addr}/APW/APWServlet/aps-autopilot-userclient/openAPI/v1/auth/tokens"
        request_header = {'Content-Type': 'application/json', 'Cookie': token}
        request_body = {}
        request = RequestInfo(request_url=url, request_body=request_body, request_header=request_header,
                              request_method=RequestMethodType.GET)
        ret, body, header = send_request(request)
        if ret and body.get("code") == "SUCCESS":
            log.error(f"Failed check Token{body}!")
            return True
        else:
            return False

    @staticmethod
    def config_extend_info(host, port, xbsa_file_path):
        if check_command_injection(xbsa_file_path):
            log.error("The xbsa_file_path has injection problem")
            raise Exception("The xbsa_file_path has injection problem")
        # 兼容老版本，当xbsa_file_path字段没传时，取默认值
        if xbsa_file_path == '':
            log.info("Roach xbsa conf path is none")
            xbsa_file_path = RoachConstant.XBSA_FILE_DEFAULT_PATH
        token_body_backupset = {"key": "BackupSet", "value": xbsa_file_path}
        token_body_route_value = {"ip": host, "port": port}
        token_body_route = {"key": "XbsaRoute", "value": json.dumps(token_body_route_value)}
        return token_body_backupset, token_body_route

    @staticmethod
    def deal_with_error_message(error_message):
        # 从错误码字典识别报错场景，未识别报默认错误码
        error_code = ResourceInfo.get_error_code_from_dict(error_message)
        if not error_code:
            log.info(f"get common error_code")
            return ErrorCode.ERR_BACKUP_RESTORE
        log.info(f"get new error_code: {error_code}")
        return error_code

    @staticmethod
    def get_error_code_from_dict(tpops_error_code):
        error_code_dict = ErrorCodeType.error_code_type_dict
        error_code = None
        for key, value in error_code_dict.items():
            if key in tpops_error_code:
                error_code = value
        return error_code

    @staticmethod
    def deal_with_task_result(ret_body):
        error_message = ret_body.get("message")
        if error_message is None:
            error_message = ret_body.get("error_msg")
        error_code = ret_body.get("code")
        if error_code is None:
            error_code = ret_body.get("error_code")
        log.info(f"get error_message: {error_message}")
        log.info(f"get error_code: {error_code}")
        if error_code and error_code != 'SUCCESS':
            raise Exception(error_message)
        return ret_body

    def get_token_info_from_local(self, cache_area):
        # 从cache仓拿取cache信息，若cache仓未存储token信息，从生产环境获取
        token_info_path = os.path.join(cache_area, 'tokenInfo')
        if not os.path.exists(token_info_path):
            log.error(f"token_info_path not exist.")
            return self.get_token_info_from_remote(cache_area)
        token = read_file(token_info_path)
        return token

    def get_token_info_from_remote(self, cache_area):
        log.info("start to get_token_info_from_remote")
        body = ResourceInfo.get_tokens_json(self.pid, self.user_name, self._addr, self.db_version)
        if not body:
            log.error("Failed get Token!")
            return {}
        if self.db_version == VERSION.TPOPS:
            token_value = body.get("data")[0].get("token")
        else:
            token_value = body.get("x_mini_token")
        if not token_value:
            log.error("Failed get Token!")
            return {}
        if self.db_version == VERSION.TPOPS:
            token = f'Authorization="{token_value}"'
        else:
            token = token_value
        token_info_path = os.path.join(cache_area, 'tokenInfo')
        exec_overwrite_file(token_info_path, token, json_flag=False)
        log.info("end to get_token_info_from_remote")
        return token

    def get_token_info(self):
        body = ResourceInfo.get_tokens_json(self.pid, self.user_name, self._addr, self.db_version)
        if not body:
            log.error("Failed get Token!")
            return {}
        if self.db_version == VERSION.TPOPS:
            token_value = body.get("data")[0].get("token")
        else:
            token_value = body.get("x_mini_token")
        if not token_value:
            log.error("Failed get Token!")
            return {}
        if self.db_version == VERSION.TPOPS:
            token = f'Authorization="{token_value}"'
        else:
            token = token_value
        return token

    def get_project_id(self):
        # 融合版本直接返回空值
        if not self.db_version or self.db_version == VERSION.CONVERGENT:
            return ""

        body = ResourceInfo.get_tokens_json(self.pid, self.user_name, self._addr, self.db_version)
        if not body:
            log.error("Failed get project_id!")
            return ""
        project_id = body.get("data")[0].get("projectId")
        if not project_id:
            log.error("Failed get project_id!")
            return ""
        return project_id

    def get_db_version(self):
        result, password_error = ResourceInfo.access_to_convergent(self.pid, self.user_name, self._addr)
        if password_error:
            return None
        if result:
            log.info("db version is convergent.")
            return VERSION.CONVERGENT
        result, password_error = ResourceInfo.access_to_tpops(self.pid, self.user_name, self._addr)
        if password_error:
            return None
        if result:
            log.info("db version is tpops.")
            return VERSION.TPOPS
        raise Exception(f'get version failed.')

    @retry_when_exception(retry_times=5, delay=random.randint(15, 30))
    def get_instance_json(self, offset):
        # 查询实例列表
        token = self.get_token_info()
        project_id = self.project_id
        limit = QUERY_SIZE
        if not token:
            log.error("No token when get_instance_json")
            return {}
        if self.db_version == VERSION.TPOPS:
            if not project_id:
                log.error("No project_id when get_instance_json")
                return {}
            url = f'{self._addr}/APW/gaussdb/v3/{project_id}/instances?offset={offset}&limit={limit}'
            request_header = {'Content-Type': 'application/json', 'Cookie': token}
        else:
            url = f'{self._addr}/rds/v3/instances?offset={offset}&limit={limit}'
            request_header = {'Content-Type': 'application/json', 'X-Mini-Token': token}
        request_body = {
            "offset": offset,
            "limit": QUERY_SIZE
        }
        request = RequestInfo(request_url=url, request_body=request_body, request_header=request_header,
                              request_method=RequestMethodType.GET)
        ret, ret_body, header = send_request(request)
        clear(token)
        if not ret:
            log.error("Failed request for instances info list!")
            raise Exception("get token failed")
        else:
            # 响应{ "instances": [ { "name": "gauss", "id": "8e940ff4-3936-49eb-9ad9-e5bb7d0d8382" } ], "total_count": 1 }
            return ret_body

    @retry_when_exception(retry_times=5, delay=random.randint(15, 30))
    def set_instance_extend_info(self, instance_id, token_body):
        token = self.get_token_info()
        project_id = self.project_id
        if not token:
            log.error("No token when set_instance_extend_info")
            return {}
        if self.db_version == VERSION.TPOPS:
            if not project_id:
                log.error("No project_id when set_instance_extend_info")
                return {}
            url = f'{self._addr}/APW/gaussdb/v3/{project_id}/instances/{instance_id}/extend-info'
            request_header = {'Content-Type': 'application/json', 'Cookie': token}
        else:
            url = f'{self._addr}/rds/gaussdb/v3/instances/{instance_id}/extend-info'
            request_header = {'Content-Type': 'application/json', 'X-Mini-Token': token}
        request_body = token_body
        request = RequestInfo(request_url=url, request_body=request_body, request_header=request_header,
                              request_method=RequestMethodType.PUT, request_timeout=TPOPS_TIMEOUT_SIXTY_SECONDS)
        ret, ret_body, ret_header = send_request(request)
        clear(token)
        if not ret:
            log.error("Failed set instance extend info!")
            raise Exception("get token failed")
        return ret_body

    def set_instance_extend_info_twice(self, instance_id, host, port, client_crt, xbsa_file_path):
        # 设置实例拓展信息
        token_body_backupset, token_body_route = self.config_extend_info(host, port, xbsa_file_path)
        self.set_instance_extend_info(instance_id, token_body_backupset)
        self.set_instance_extend_info(instance_id, token_body_route)
        if not (client_crt and self.client_key and self.rand_pass and self.ca_cert_pem):
            log.error("client_crt is none")
            return True
        try:
            self.set_ssl_cert_info(instance_id, client_crt)
        except Exception as ex:
            log.error(f"set_ssl_cert_info failed, exception: {ex}")
        return True

    @retry_when_exception(retry_times=5, delay=random.randint(15, 30))
    def set_ssl_cert_info(self, instance_id, client_crt):
        # 设置ssl证书
        token = self.get_token_info()
        project_id = self.project_id
        if not token:
            log.error("No token when set_ssl_cert_info")
            return {}
        set_ssl_cert_body = {
            "ca_cert_pem": self.ca_cert_pem, "client_crt": client_crt, "client_key": self.client_key,
            "rand_pass": self.rand_pass
        }
        if self.db_version == VERSION.TPOPS:
            if not project_id:
                log.error("No project_id when set_ssl_cert_info")
                return {}
            set_ssl_cert_header = {'Content-Type': 'application/json', 'Cookie': token}
            set_ssl_cert_url = f'{self._addr}/APW/gaussdb/v3/' \
                               f'{project_id}/instances/{instance_id}/backups/ssl-certs'
        else:
            set_ssl_cert_header = {'Content-Type': 'application/json', 'X-Mini-Token': token}
            set_ssl_cert_url = f'{self._addr}/rds/gaussdb/v3/instances/{instance_id}/backups/ssl-certs'
        request = RequestInfo(request_url=set_ssl_cert_url, request_body=set_ssl_cert_body,
                              request_header=set_ssl_cert_header,
                              request_method=RequestMethodType.POST, request_timeout=TPOPS_TIMEOUT_SIXTY_SECONDS)
        ret, ret_body, header = send_request(request)
        clear(token)
        if not ret:
            log.error("Failed set ssl cert!")
            raise Exception("get token failed")
        return ret_body

    @retry_when_exception(retry_times=5, delay=random.randint(15, 30))
    def request_instance_extend_info(self, instance_id, key):
        # 请求实例拓展信息
        token = self.get_token_info()
        project_id = self.project_id
        if not token:
            log.error("No token when request_instance_extend_info")
            return {}
        if self.db_version == VERSION.TPOPS:
            if not project_id:
                log.error("No project_id when request_instance_extend_info")
                return {}
            url = f'{self._addr}/APW/gaussdb/v3/{project_id}/instances/{instance_id}/extend-info?key={key}'
            request_header = {'Content-Type': 'application/json', 'Cookie': token}
        else:
            url = f'{self._addr}/rds/gaussdb/v3/instances/{instance_id}/extend-info?key={key}'
            request_header = {'Content-Type': 'application/json', 'X-Mini-Token': token}
        request_body = {}
        request = RequestInfo(request_url=url, request_body=request_body, request_header=request_header,
                              request_method=RequestMethodType.GET, request_timeout=TPOPS_TIMEOUT_SIXTY_SECONDS)
        ret, ret_body, header = send_request(request)
        clear(token)
        if not ret:
            log.error("Failed request for instance extend info!")
            raise Exception("get token failed")
        return ret_body

    def get_instance_extend_info_twice(self, instance_id):
        # 获取实例拓展信息
        backup_info = self.request_instance_extend_info(instance_id, ExtendInfoKeys.BACKUP)
        xbsa_info = self.request_instance_extend_info(instance_id, ExtendInfoKeys.XBSA)
        if not (backup_info and xbsa_info):
            log.error(f"Failed get extend_info for instance {instance_id}!")
            return {}
        extend_info = {ExtendInfoKeys.BACKUP: backup_info.get("value"), ExtendInfoKeys.XBSA: xbsa_info.get("value")}
        return extend_info

    @retry_when_exception(retry_times=5, delay=random.randint(15, 30))
    def get_backup_list(self, instance_id, backup_id=''):
        # 获取全量备份列表, 按实例
        token = self.get_token_info()
        project_id = self.project_id
        if not token:
            log.error("No token when get_backup_list")
            return {}
        if self.db_version == VERSION.TPOPS:
            if not project_id:
                log.error("No project_id when get_backup_list")
                return {}
            url = f"{self._addr}/APW/gaussdb/v3/{project_id}/backups?instance_id={instance_id}"
            request_header = {'Content-Type': 'application/json', 'Cookie': token}
        else:
            url = f'{self._addr}/rds/gaussdb/v3.1/backups?backup_type=Snapshot_Xbsa&instance_id={instance_id}'
            request_header = {'Content-Type': 'application/json', 'X-Mini-Token': token}
        # 当传入backup_id时将backup_id传给数据库
        if backup_id:
            url = f'{url}&backup_id={backup_id}'
        request_body = {}
        request = RequestInfo(request_url=url, request_body=request_body, request_header=request_header,
                              request_method=RequestMethodType.GET, request_timeout=TPOPS_TIMEOUT_SIXTY_SECONDS)
        ret, ret_body, header = send_request(request)
        clear(token)
        if not ret:
            log.error(f"Failed get backup list for instance {instance_id}!")
            raise Exception("get token failed")
        return ret_body

    @retry_when_exception(retry_times=5, delay=random.randint(15, 30))
    def get_log_backup_list(self, instance_id):
        # 获取全量备份列表, 按实例
        token = self.get_token_info()
        project_id = self.project_id
        if not token:
            log.error("No token when get_log_backup_list")
            return {}
        if self.db_version == VERSION.TPOPS:
            if not project_id:
                log.error("No project_id when get_log_backup_list")
                return {}
            url = f"{self._addr}/APW/gaussdb/v3/{project_id}/backups?instance_id={instance_id}&backup_type=LOG_XBSA"
            request_header = {'Content-Type': 'application/json', 'Cookie': token}
        else:
            url = f'{self._addr}/rds/gaussdb/v3.1/backups?instance_id={instance_id}&backup_type=Log_Xbsa'
            request_header = {'Content-Type': 'application/json', 'X-Mini-Token': token}
        request_body = {}
        request = RequestInfo(request_url=url, request_body=request_body, request_header=request_header,
                              request_method=RequestMethodType.GET, request_timeout=TPOPS_TIMEOUT_SIXTY_SECONDS)
        ret, ret_body, header = send_request(request)
        clear(token)
        if not ret:
            log.error(f"Failed get backup list for instance {instance_id}!")
            raise Exception("get token failed")
        return ret_body

    @retry_when_exception(retry_times=5, delay=random.randint(15, 30))
    def get_diff_backup_list(self, instance_id, limit=-1):
        # 获取差异备份列表, 按实例
        token = self.get_token_info()
        project_id = self.project_id
        if not token:
            log.error("No token when get_diff_backup_list")
            return {}
        if self.db_version == VERSION.TPOPS:
            if not project_id:
                log.error("No project_id when get_diff_backup_list")
                return {}
            url = f'{self._addr}/APW/gaussdb/v3/{project_id}/instances/{instance_id}/differential-backups'
            request_header = {'Content-Type': 'application/json', 'Cookie': token}
        else:
            url = f'{self._addr}/rds/gaussdb/v3/instances/{instance_id}/differential-backups'
            request_header = {'Content-Type': 'application/json', 'X-Mini-Token': token}
        if limit > 0:
            url = f'{url}?limit={limit}'
        request_body = {}
        request = RequestInfo(request_url=url, request_body=request_body, request_header=request_header,
                              request_method=RequestMethodType.GET, request_timeout=TPOPS_TIMEOUT_SIXTY_SECONDS)
        ret, ret_body, header = send_request(request)
        clear(token)
        if not ret:
            log.error(f"Failed get differential backup list for instance {instance_id}!")
            raise Exception("get token failed")
        return ret_body

    @retry_when_exception(retry_times=5, delay=random.randint(15, 30))
    def get_job_info(self, job_id):
        # 获取任务进度
        token = self.get_token_info()
        project_id = self.project_id
        if not token:
            log.error("No token when get_job_info")
            return {}
        if self.db_version == VERSION.TPOPS:
            if not project_id:
                log.error("No project_id when get_job_info")
                return {}
            url = f"{self._addr}/APW/gaussdb/v3/{project_id}/jobs?id={job_id}"
            request_header = {'Content-Type': 'application/json', 'Cookie': token}
        else:
            url = f'{self._addr}/rds/v3/jobs?id={job_id}'
            request_header = {'Content-Type': 'application/json', 'X-Mini-Token': token}
        request_body = {}
        request = RequestInfo(request_url=url, request_body=request_body, request_header=request_header,
                              request_method=RequestMethodType.GET, request_timeout=TPOPS_TIMEOUT_SIXTY_SECONDS)
        ret, ret_body, header = send_request(request)
        clear(token)
        if not ret:
            log.error(f"Failed get progress for job {job_id}")
            raise Exception("get token failed")
        if ret_body.get("code"):
            raise Exception(f"get job info fail, ret_body: {ret_body}")
        return ret_body

    @retry_when_exception(retry_times=5, delay=random.randint(15, 30))
    def request_backup_job(self, instance_id, request_body):
        # 请求备份任务
        token = self.get_token_info()
        project_id = self.project_id
        if not token:
            log.error("No token when request_backup_job")
            return {}
        if self.db_version == VERSION.TPOPS:
            if not project_id:
                log.error("No project_id when request_backup_job")
                return {}
            url = f"{self._addr}/APW/gaussdb/v3/{project_id}/backups"
            request_header = {'Content-Type': 'application/json', 'Cookie': token}
        else:
            url = f'{self._addr}/rds/gaussdb/v3/backups'
            request_header = {'Content-Type': 'application/json', 'X-Mini-Token': token}
        # 请求body后两个参数非必须，可为空
        request = RequestInfo(request_url=url, request_body=request_body, request_header=request_header,
                              request_method=RequestMethodType.POST, request_timeout=TPOPS_TIMEOUT_SIXTY_SECONDS)
        ret, ret_body, header = send_request(request)
        log.info(f"request_backup_job ret : {ret} ,ret_body:{ret_body}, header:{header}")
        clear(token)
        result = self.deal_with_task_result(ret_body)
        return result

    @retry_when_exception(retry_times=5, delay=random.randint(15, 30))
    def request_restore_job(self, instance_id, backup_id, request_body):
        # 请求备份任务
        token = self.get_token_info()
        project_id = self.project_id
        if not token:
            log.error("No token when request_restore_job")
            return {}
        if self.db_version == VERSION.TPOPS:
            if not project_id:
                log.error("No project_id when request_restore_job")
                return {}
            url = f"{self._addr}/APW/gaussdb/v3/{project_id}/instances/recovery"
            request_header = {'Content-Type': 'application/json', 'Cookie': token}
        else:
            url = f'{self._addr}/rds/gaussdb/v3/instances/recovery'
            request_header = {'Content-Type': 'application/json', 'X-Mini-Token': token}
        log.info(f"request_restore_job restore_url:{url}, restore_body:{request_body}")
        request = RequestInfo(request_url=url, request_body=request_body, request_header=request_header,
                              request_method=RequestMethodType.POST, request_timeout=TPOPS_TIMEOUT_SIXTY_SECONDS)
        ret, ret_body, header = send_request(request)
        log.info(f"request_restore_job ret : {ret}, header : {header}, ret_body:{ret_body}")
        clear(token)
        result = self.deal_with_task_result(ret_body)
        return result

    @retry_when_exception(retry_times=5, delay=random.randint(15, 30))
    def change_backup_media(self, instance_id, request_body):
        if self.db_version == VERSION.TPOPS:
            return {}
        token = self.get_token_info()
        if not token:
            log.error("No token when request_restore_job")
            return {}
        log.info(f"xbsa : request_body :{request_body}")
        url = f'{self._addr}/rds/gaussdb/v3/instances/{instance_id}/change-backup-media'
        request_header = {'Content-Type': 'application/json', 'X-Mini-Token': token}
        request = RequestInfo(request_url=url, request_body=request_body, request_header=request_header,
                              request_method=RequestMethodType.PUT, request_timeout=TPOPS_TIMEOUT_SIXTY_SECONDS)
        ret, ret_body, header = send_request(request)
        log.info(f"xbsa: ret :{ret}, ret_body: {ret_body}, header:{header}")
        clear(token)
        if not ret:
            log.error(f"Failed change backup media for instance {instance_id}!")
            raise Exception("change backup media failed")
        if "job_id" not in ret_body:
            if ret_body.get("errCode") != GaussDBRdsErrorCode.REPEAT_CHANGE_BACKUP_MEDIA_TO_XBSA:
                log.error(f"Set xbsa backup media failed. {ret_body}")
                raise Exception(f"Set xbsa backup media failed. {ret_body}")
        return ret_body

    @retry_when_exception(retry_times=5, delay=random.randint(15, 30))
    def copy_backup_meta_info_to_new_cluster(self, instance_id, request_body):
        if self.db_version == VERSION.TPOPS:
            return {}
        token = self.get_token_info()
        if not token:
            log.error("No token when request_restore_job")
            return {}
        url = f'{self._addr}/rds/gaussdb/v3/backups/sync-metadata'
        request_header = {'Content-Type': 'application/json', 'X-Mini-Token': token}
        request = RequestInfo(request_url=url, request_body=request_body, request_header=request_header,
                              request_method=RequestMethodType.POST, request_timeout=TPOPS_TIMEOUT_SIXTY_SECONDS)
        ret, ret_body, header = send_request(request)
        if not ret:
            log.error(f"Failed change backup media for instance {instance_id}!")
            raise Exception(f"copy backup meta info to new cluster failed, {ret_body}")
        return ret_body

    @retry_when_exception(retry_times=5, delay=random.randint(15, 30))
    def set_gaussdb_conf(self, instance_id, backup_policy):
        log.info(f"start set_gaussdb_conf, backup_policy is {backup_policy}")
        if self.db_version == VERSION.TPOPS:
            return True
        rate_limit = backup_policy.get("ext_parameters").get("rate_limit")
        enable_standby_backup = backup_policy.get("ext_parameters").get("enable_standby_backup", False)
        close_compression = backup_policy.get("ext_parameters").get("close_compression", False)
        if rate_limit is None:
            log.info(f"Rate limit is None, use default value")
            rate_limit = BACKUP_RATE_LIMIT_NORMAL
        if not all([isinstance(rate_limit, int),
                    isinstance(enable_standby_backup, bool),
                    isinstance(close_compression, bool),
                    BACKUP_RATE_LIMIT_MIN <= rate_limit <= BACKUP_RATE_LIMIT_MAX]):
            log.error("Param error.")
            return False
        token = self.get_token_info()
        if not token:
            log.error("No token when request_restore_job")
            return False
        url = f"{self._addr}/rds/gaussdb/v3/instances/{instance_id}/backups/config"
        request_header = {'Content-Type': 'application/json', 'X-Mini-Token': token}
        request_body = {
            "rate_limit": rate_limit,
            "enable_standby_backup": enable_standby_backup,
            "close_compression": close_compression
        }
        log.info(f"Get request body {request_body}.")
        request = RequestInfo(request_url=url, request_body=request_body, request_header=request_header,
                              request_method=RequestMethodType.PUT, request_timeout=TPOPS_TIMEOUT_SIXTY_SECONDS)
        ret, ret_body, header = send_request(request)
        log.info(f"Set gaussdb backup config result: {ret_body}.")
        if not ret or 'DBS.200856' in ret_body:
            # 低数据库引擎版本，不支持设置是否压缩参数
            log.info(f"Failed set gauss db config for instance {instance_id}, start to use lower version!")
            request_body = {
                "rate_limit": rate_limit,
                "enable_standby_backup": enable_standby_backup
            }
            request = RequestInfo(request_url=url, request_body=request_body, request_header=request_header,
                                  request_method=RequestMethodType.PUT, request_timeout=TPOPS_TIMEOUT_SIXTY_SECONDS)
            ret, ret_body, header = send_request(request)
            log.info(f"Set gaussdb backup config result: {ret_body}.")
            if not ret:
                clear(token)
                raise Exception("Set gauss db config failed.")
        clear(token)
        log.info("Set gauss db config successful.")
        return True
